package com.xinqi.modules.prepare.folder.service.impl;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.xinqi.common.base.dto.CommonFileDTO;
import com.xinqi.common.base.response.PageInfo;
import com.xinqi.common.base.response.Param;
import com.xinqi.common.base.utils.json.JSON;
import com.xinqi.common.base.utils.tree.TreeHelp;
import com.xinqi.common.core.utils.DateUtils;
import com.xinqi.common.core.utils.MessageUtils;
import com.xinqi.common.core.utils.StringUtils;
import com.xinqi.common.mybatis.utils.Pages;
import com.xinqi.common.mybatis.utils.Queries;
import com.xinqi.common.satoken.utils.LoginHelper;
import com.xinqi.modules.prepare.common.enums.PrepareLimitEnum;
import com.xinqi.modules.prepare.common.service.PrepareAuthService;
import com.xinqi.modules.prepare.common.utils.CommonFileUtils;
import com.xinqi.modules.prepare.folder.convert.PrepareFileFolderConvert;
import com.xinqi.modules.prepare.folder.domain.PrepareFileFolderEntity;
import com.xinqi.modules.prepare.folder.dto.req.PrepareFileFolderCopyDTO;
import com.xinqi.modules.prepare.folder.dto.req.PrepareFileFolderCreateDTO;
import com.xinqi.modules.prepare.folder.dto.req.PrepareFileFolderDeleteDTO;
import com.xinqi.modules.prepare.folder.dto.req.PrepareFileFolderMoveDTO;
import com.xinqi.modules.prepare.folder.dto.req.PrepareFileFolderQueryDTO;
import com.xinqi.modules.prepare.folder.dto.req.PrepareFileFolderUpdateDTO;
import com.xinqi.modules.prepare.folder.dto.req.PrepareFileShareURLDto;
import com.xinqi.modules.prepare.folder.dto.rsp.PrepareFileFolderResultDTO;
import com.xinqi.modules.prepare.folder.manager.PrepareFileFolderManager;
import com.xinqi.modules.prepare.folder.service.PrepareFileFolderService;
import com.xinqi.modules.prepare.prepare.enums.PrepareFileTypeEnum;
import com.xinqi.modules.prepare.prepare.enums.PrepareMemberPermissionEnum;
import com.xinqi.modules.prepare.prepare.enums.PrepareTypeEnum;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.validation.Valid;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

import static com.xinqi.common.base.error.CommonErrors.BAD_REQUEST;

/**
 * 备课区文件夹服务层实现
 *
 * @author: system
 * @date: 2023/03/19
 */
@Validated
@Service("prepareFileFolderService")
@RequiredArgsConstructor
public class PrepareFileFolderServiceImpl implements PrepareFileFolderService {
    private final PrepareFileFolderConvert prepareFileFolderConvert;
    private final PrepareFileFolderManager prepareFileFolderManager;
    private final PrepareAuthService prepareAuthService;

    /**
     * 分页查询 备课区文件夹数据
     */
    @Override
    @DS("slaver")
    public PageInfo<PrepareFileFolderResultDTO> page(Param pageable, PrepareFileFolderQueryDTO query) {
        IPage<PrepareFileFolderResultDTO> page = prepareFileFolderManager.findPage(Pages.page(pageable), query);
        return Pages.convert(page);
    }

    /**
     * 查询 备课区文件夹数据
     */
    @Override
    public List<PrepareFileFolderResultDTO> find(PrepareFileFolderQueryDTO query) {
        return prepareFileFolderManager.findList(query);
    }

    private LambdaQueryWrapper<PrepareFileFolderEntity> wrapper(PrepareFileFolderQueryDTO query) {
        LambdaQueryWrapper<PrepareFileFolderEntity> wrapper = Queries.lambda();
        Queries.accept(query.getId(), v -> wrapper.eq(PrepareFileFolderEntity::getId, v));
        return wrapper;
    }

    /**
     * 根据id查询 备课区文件夹数据
     */
    @Override
    public PrepareFileFolderResultDTO findById(Long id) {
        BAD_REQUEST.check(Objects.nonNull(id), "ID不能为空");
        return Optional.ofNullable(prepareFileFolderManager.findById(id)).map(prepareFileFolderConvert::copy).orElse(null);
    }

    /**
     * 新增 备课区文件夹数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long insert(@Valid PrepareFileFolderCreateDTO dto) {
        PrepareFileFolderEntity entity = prepareFileFolderConvert.create(dto);
        entity.setFileType(PrepareFileTypeEnum.FOLDER);
        // 校验
        check(entity);
        // 处理文件路劲
        setFilePath(entity);
        prepareFileFolderManager.save(entity);
        return entity.getId();
    }

    private void setFilePath(PrepareFileFolderEntity entity) {
        if (entity.getPid() == 0L) {
            entity.setFilePath(CommonFileUtils.filePath(entity.getName()));
            entity.setLevel(CommonFileUtils.fileLevel(entity.getFilePath()));
            return;
        }
        PrepareFileFolderEntity parent = prepareFileFolderManager.findById(entity.getPid());
        if (parent == null) {
            throw BAD_REQUEST.asException("父类节点不存在");
        }
        BAD_REQUEST.check(Objects.equals(parent.getType(), entity.getType()), "父类型不匹配");
        entity.setFilePath(CommonFileUtils.filePath(entity.getName(), parent.getFilePath()));
        entity.setLevel(CommonFileUtils.fileLevel(entity.getFilePath()));
        // 只有文件夹才校验深度
        if (Objects.equals(entity.getFileType(), PrepareFileTypeEnum.FOLDER)) {
            PrepareLimitEnum.文件夹深度.check(entity.getLevel());
        }
    }

    private void check(PrepareFileFolderEntity entity) {
        if (entity.getPid() == null) {
            entity.setPid(0L);
        }
        BAD_REQUEST.check(entity.getFileType() != null, "文件类型不能为空");
        BAD_REQUEST.check(!entity.getName().contains("/"), "文件名称不能包含'/'字符");
        BAD_REQUEST.check(!isExistName(entity.getPrepareId(), entity.getType(), entity.getName(), entity.getPid(), entity.getFileType(), entity.getId()), "文件名已存在");
    }

    /**
     * 是否存在文件名
     */
    @Override
    public Boolean isExistName(Long prepareId, PrepareTypeEnum type, String name, Long pid, PrepareFileTypeEnum fileType, Long oId) {
        return prepareFileFolderManager.isExistName(prepareId, type, name, pid, fileType, oId);
    }

    /**
     * 根据id修改 备课区文件夹数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateFolderById(@Valid PrepareFileFolderUpdateDTO dto) {
        PrepareFileFolderEntity entity = Optional.ofNullable(prepareFileFolderManager.getById(dto.getId())).orElseThrow(() -> BAD_REQUEST.asException("文件件不存在"));
        BAD_REQUEST.check(Objects.equals(entity.getFileType(), PrepareFileTypeEnum.FOLDER), "只能修改文件夹类型");
        prepareAuthService.check(entity.getPrepareId(), PrepareMemberPermissionEnum.编辑);
        if (Objects.equals(entity.getName(), dto.getName())) {
            return true;
        }
        entity.setName(dto.getName());
        // 校验
        check(entity);
        // 设置路径
        setFilePath(entity);
        List<PrepareFileFolderEntity> list = new ArrayList<>();
        list.add(entity);
        // 修改子文件的路径
        prepareFileFolderManager.setChildrenPath(list, entity.getId(), entity.getFilePath());
        return prepareFileFolderManager.updateBatchById(list);
    }

    /**
     * 根据id删除 备课区文件夹数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteById(Long id) {
        BAD_REQUEST.check(Objects.nonNull(id), "ID不能为空");
        PrepareFileFolderEntity entity = Optional.ofNullable(prepareFileFolderManager.getById(id)).orElseThrow(() -> BAD_REQUEST.asException("数据不存在"));
        prepareAuthService.check(entity.getPrepareId(), PrepareMemberPermissionEnum.删除);
        prepareFileFolderManager.deleteAllChild(entity);
        return true;
    }

    /**
     * 根据id删除 备课区文件夹数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteById(PrepareFileFolderDeleteDTO dto) {
        return deleteById(dto.getId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean move(@Valid PrepareFileFolderMoveDTO dto) {
        BAD_REQUEST.check(!Objects.equals(dto.getId(), dto.getTargetPid()), "源ID和目标ID不能相同");
        PrepareFileFolderEntity source = Optional.ofNullable(prepareFileFolderManager.findById(dto.getId())).orElseThrow(() -> BAD_REQUEST.asException("文件夹或文件不存在"));
        BAD_REQUEST.check(Objects.equals(source.getType(), dto.getType()), "备课区类型不一致");
        prepareAuthService.check(source.getPrepareId(), PrepareMemberPermissionEnum.移动到);
        prepareAuthService.check(dto.getTargetPrepareId(), PrepareMemberPermissionEnum.新建);
        // 校验是否移动到自己的子文件夹
        if (dto.getTargetPid() != 0) {
            PrepareFileFolderEntity target = Optional.ofNullable(prepareFileFolderManager.findById(dto.getTargetPid()))
                .orElseThrow(() -> BAD_REQUEST.asException("文件夹或文件不存在"));
            if (Objects.equals(source.getPrepareId(), target.getPrepareId())
                && Objects.equals(source.getFileType(), PrepareFileTypeEnum.FOLDER)
                && source.getFilePath().startsWith(target.getFilePath())) {
                throw BAD_REQUEST.asException("不能移动到自己的子文件夹");
            }
        }
        prepareFileFolderManager.move(source, dto.getTargetPrepareId(), dto.getTargetPid());
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean copy(@Valid PrepareFileFolderCopyDTO dto) {
        BAD_REQUEST.check(!Objects.equals(dto.getId(), dto.getTargetPid()), "源ID和目标ID不能相同");
        PrepareFileFolderEntity source = Optional.ofNullable(prepareFileFolderManager.findById(dto.getId())).orElseThrow(() -> BAD_REQUEST.asException("文件夹或文件不存在"));
        BAD_REQUEST.check(Objects.equals(source.getType(), dto.getType()), "备课区类型不一致");
        prepareAuthService.check(source.getPrepareId(), PrepareMemberPermissionEnum.复制到);
        prepareAuthService.check(dto.getTargetPrepareId(), PrepareMemberPermissionEnum.新建);
        prepareFileFolderManager.copy(source, dto.getTargetPrepareId(), dto.getTargetPid());
        return true;
    }

    @Override
    public List<CommonFileDTO> download(Long id) {
        BAD_REQUEST.check(Objects.nonNull(id), "ID不能为空");
        PrepareFileFolderEntity source = Optional.ofNullable(prepareFileFolderManager.findById(id)).orElseThrow(() -> BAD_REQUEST.asException(MessageUtils.message("remind.content.prepare.not.blank")));
        BAD_REQUEST.check(Objects.equals(source.getFileType(), PrepareFileTypeEnum.FILE), "只能下载文件");
        prepareAuthService.check(source.getPrepareId(), PrepareMemberPermissionEnum.下载);
        if (StringUtils.isNotEmpty(source.getFiles())) {
            return Collections.emptyList();
        }
        return JSON.parseList(source.getFiles(), CommonFileDTO.class);
    }

    @Override
    public String generateShareUrl(PrepareFileShareURLDto dto) {
        PrepareFileFolderEntity source = Optional.ofNullable(prepareFileFolderManager.findById(dto.getId())).orElseThrow(() -> BAD_REQUEST.asException("文件不存在"));

        return "?" +
            "itemName=" + source.getName() +
            "&itemId=" + dto.getId() +
            "&itemType=" + dto.getType() +
            "&deadline=" + dto.getDeadline() +
            "&shareNickname=" + LoginHelper.getNickname() +
            "&shareUserId=" + LoginHelper.getUserId();
    }

    @Override
    public String visitShareContent(PrepareFileShareURLDto dto) {
        BAD_REQUEST.check(dto.getDeadline() >= DateUtils.getNowDateTime(), "此链接已失效");
        PrepareFileFolderEntity source = Optional.ofNullable(prepareFileFolderManager.findById(dto.getId())).orElseThrow(() -> BAD_REQUEST.asException("文件不存在"));

        return "?" +
            "itemName=" + source.getName() +
            "&itemId=" + dto.getId() +
            "&itemType=" + dto.getType() +
            "&deadline=" + dto.getDeadline() +
            "&shareNickname=" + dto.getNickname() +
            "&shareUserId=" + dto.getUserId();
    }

    @Override
    public List<PrepareFileFolderResultDTO> tree(PrepareFileFolderQueryDTO dto) {
        dto.setId(null);
        dto.setName(null);
        dto.setPid(null);
        List<PrepareFileFolderResultDTO> list = find(dto);
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyList();

        }
        List<PrepareFileFolderResultDTO> tree = TreeHelp.<PrepareFileFolderResultDTO, Long>builder()
            .setList(list)
            .setChildren("children")
            .setParent("pid")
            .setComparator(Comparator.comparing(PrepareFileFolderResultDTO::getId))
            .build()
            .handle(0L);
        return tree;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean batchDelete(Long prepareId, List<Long> idList) {
        BAD_REQUEST.check(CollectionUtils.isNotEmpty(idList), "要删除的id不能为空");
        List<PrepareFileFolderEntity> entityList = Optional.ofNullable(prepareFileFolderManager.findByIds(idList)).orElseThrow(() -> BAD_REQUEST.asException("数据不存在"));

        prepareAuthService.check(prepareId, PrepareMemberPermissionEnum.删除);
        entityList.forEach(prepareFileFolderManager::deleteAllChild);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean batchMove(List<PrepareFileFolderMoveDTO> dto) {
        BAD_REQUEST.check(CollectionUtils.isNotEmpty(dto), "要移动的文件列表不能为空");
        dto.forEach(this::move);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean batchCopy(List<PrepareFileFolderCopyDTO> dto) {
        BAD_REQUEST.check(CollectionUtils.isNotEmpty(dto), "要复制的文件列表不能为空");
        dto.forEach(this::copy);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean savToPrepare(PrepareFileFolderCopyDTO dto) {
        PrepareFileFolderEntity source = Optional.ofNullable(prepareFileFolderManager.findById(dto.getId())).orElseThrow(() -> BAD_REQUEST.asException("文件夹或文件不存在"));
        BAD_REQUEST.check(Objects.equals(source.getType(), dto.getType()), "备课区类型不一致");
        prepareFileFolderManager.copy(source, dto.getTargetPrepareId(), dto.getTargetPid());
        return true;
    }
}
